@page "/p/{PollId}"

@using Orleans.Runtime
@using OrleansVoting.Data
@using Orleans
@using OrleansVoting

@implements IAsyncDisposable

@inject PollService PollService
@inject DemoService DemoService

@if (!string.IsNullOrWhiteSpace(_errorMessage))
{
	<div class="alert alert-danger" role="alert">
		@_errorMessage
	</div>
}
else if (!_loaded)
{
	<h2>Loading poll @(PollId)...</h2>
}
else if (!_voted)
{
	<h1>@_results.Question</h1>
	<br />
	<div>
		@foreach (var (index, option) in _results.Options.Select((value, index) => (index, value)))
		{
			<div class="input-group mb-3">
				<button class="btn btn-outline-secondary" type="button" id="button-add" @onclick="@(e => VoteForOption(index))">👍</button>
				<span class="input-group-text col-9">@(option.Option)</span>
			</div>
		}
	</div>
	<br />
	<div>
		<h2>Share this poll: @PollId</h2>
	</div>
}
else
{
	<h1>@_results.Question</h1>
	<br />
	<div>
		@foreach (var (option, votes, percentage) in CalculateResults())
		{
			<div class="bg-light p-2 m-2 rounded">
				<h3>@option:</h3>
				<div class="progress" style="height: 50px;">
				  <div class="progress-bar" role="progressbar" style="width: @(percentage)%;" aria-valuenow="@percentage" aria-valuemin="0" aria-valuemax="100"><h4>@percentage% (@votes votes)</h4></div>
				</div>
			</div>
		}
	</div>
    <br />
    <br />
    <div class="border bg-light">
        <button class="btn btn-danger" @onclick="DemoTryDoubleVoting">DEMO: try double-voting</button>
        <button class="btn btn-danger" @onclick="DemoSimulateVoters">DEMO: simulate other voters</button>
    </div>
}
@code
{
    [Parameter] public string PollId { get; set; } = "Poll";
    private MyPollWatcher? _watcher;
    private IAsyncDisposable? _subscription;
    private bool _loaded;
    private bool _voted;
    private string? _errorMessage;

    private Guid ownerKey = Guid.NewGuid();
    private PollState _results = new PollState { Question = "Loading...", Options = new List<(string, int)>() };

	protected override async Task OnInitializedAsync()
	{
		try
		{
			(_results, _voted) = await PollService.GetPollResultsAsync(PollId);
			_watcher = new MyPollWatcher(this);
			_subscription = await PollService.WatchPoll(PollId, _watcher);
			_loaded = true;
            await base.OnInitializedAsync();
        }
        catch (Exception exception)
        {
            _errorMessage = exception.Message;
        }
    }

    private async Task VoteForOption(int index)
    {
        try
        {
            _results = await PollService.AddVoteAsync(PollId, index);
            _voted = true;
        }
        catch (Exception exception)
        {
            _errorMessage = exception.Message;
        }
    }

    private List<(string Option, int Votes, int Percentage)> CalculateResults()
    {
        if (_results?.Options is not {Count: > 0} options) return new();
        var totalVotes = options.Sum(o => o.Votes);
        var result = new List<(string Option, int Votes, int Percentage)>();
        foreach (var val in options)
        {
            var percentage = Math.Clamp(val.Votes * 100 / totalVotes, 0, 100);
            result.Add((val.Option, val.Votes, percentage));
        }

        return result;
    }

    public async ValueTask DisposeAsync()
    {
        if (_subscription is not null)
        {
            await _subscription.DisposeAsync();
        }
    }

    private void DemoTryDoubleVoting()
    {
        _voted = false;
    }

    private void DemoSimulateVoters()
    {
        DemoService.SimulateVoters(PollId, 200).Ignore();
    }

    private void UpdateResults(PollState results)
    {
        // We use InvokeAsync to schedule our update on the UI thread,
        // instead of updating it from a background thread, which is not allowed
        InvokeAsync(() =>
        {
            _results = results;
            this.StateHasChanged();
        });
    }

    private class MyPollWatcher : IPollWatcher
    {
        private readonly Poll _page;
        public MyPollWatcher(Poll page) => _page = page;
        public void OnPollUpdated(PollState state) => _page.UpdateResults(state);
    }
}